/**
* MemoryPool.h
*
* Great Wisdom Confidential
* Copyright 2013 Great Wisdom Corporation. All Rights Reserved.
*
* Author: Lixin Guo
* Date: 2/7/2013
* Description: 
*/

#include "GlobalDefinition.h"
#include <vector>
#include <list>

#pragma once
using namespace std;

/**  
* Constant variable to define size in the memory pool
*/
//#define DEFAULT_MEMORY_SIZE 1024*1024*1024 // 1G
const unsigned int DEFAULT_SIZE = 1024*1024*1024;	// 1G
const unsigned int DEFAULT_UNIT_SIZE = 1024*1024;	// 1M
const unsigned int DEFAULT_UNITS_IN_PAGE = 8;		// Units in one page
const unsigned int DEFAULT_GROW_SIZE = 1024*1024*80;// Increment size when ran out of original memory 

/**  
* Unit information. This is the minimum unit in the memory pool
*/
typedef struct tagUnitInfo
{
	unsigned int m_uiIndex;			// Index of unit
	bool	m_bIsAvailable;			// Is the unit available
	unsigned int m_uiUnitCount;		// Indicates how many unit in a row
	char*	pszAddress;				// Memory address of the unit

	/**
	* A constructor.
	*/
	tagUnitInfo()
		:m_uiIndex(0)
		,m_bIsAvailable(true)
		,m_uiUnitCount(1)
		,pszAddress(NULL)
	{
	}
}UnitInfo,*LPUnitInfo;

/**  
* Page information.
*/
typedef struct tagPageInfo
{
	vector<UnitInfo> m_vecUnitList;	// Container to store unit
	char* pszPageAddress;			// Memory size of the page.

	/**
	* A constructor.
	*/
	tagPageInfo()
		:m_vecUnitList()
		,pszPageAddress(NULL)
	{
	}
}PageInfo,*LPPageInfo;

/**  
* Index table to record unit associates with ID and reference count
*/
typedef struct tagIndexTable
{
	unsigned int m_uiID;		// Memory ID 
	unsigned int m_uiIndex;		// Unit index
	unsigned int m_uiRefCount;	// Reference count. Release the unit when refernce count equals to zero
	char*		 m_pszAddress;	// Unit address

	/**
	* A constructor.
	*/
	tagIndexTable()
		:m_uiID(0)
		,m_uiIndex(0)
		,m_uiRefCount(0)
		,m_pszAddress(NULL)
	{
	}
}IndexTable,*LPIndexTable;


/**  
* Class of memory pool
*/
class MemoryPool
{
public:
	/**
	* To get a static instance of memory pool by using default initialized parameters.
	* @return Instance of memory pool
	*/
	static MemoryPool* Instance();

	/**
	* To get a static instance of memory pool.
	* @param p_uiMemorySize Total size of the memory pool
	* @param p_uiUnitSize One unit size
	* @param p_uiUnitInPage Indicates how many units in one page
	* @param p_uiGrowSize Increment size for re-allocating memory.
	* @return Instance of memory pool
	*/
	static MemoryPool* Instance(unsigned int p_uiMemorySize,unsigned int p_uiUnitSize,unsigned int p_uiUnitInPage,unsigned int p_uiGrowSize);

	/**
	* Requires buffer from memory pool.
	* @param p_uiBufferSize Required size of buffer
	* @param p_uiID ID associates with the buffer.
	* @return Memory address of the buffer
	*/
	char* Allocate(unsigned int p_uiBufferSize, unsigned int p_uiID);

	/**
	* Release buffer from memory pool
	* @param p_szBufferAddress Buffer address
	* @return Indicates if this operation is successful
	*/
	bool Release(char* p_szBufferAddress);

	/**
	* Verifies if memory associates with specified ID  is available
	* @param p_uiID Memory ID
	* @return Indicates if specified ID is available
	*/
	bool IsIDAvailable(unsigned int p_uiID);

	/**
	* Print memory status to terminal. The function will help user to confirm currently memory status
	*/
	void PrintMemoryStatus();

	/**
	* Get the occupancy of memory pool
	* @return Occupancy of memory pool
	*/
	float GetMemoryStatus();

	/**
	* Release memory pool buffer
	* @return Indicates if this operation is successful
	*/
	bool ReleaseMemoryPool();

private:
	/**
	* A constructor
	* @param p_uiMemorySize Total size of the memory pool
	* @param p_uiUnitSize One unit size
	* @param p_uiUnitInPage Indicates how many units in one page
	* @param p_uiGrowSize Increment size for re-allocating memory.
	*/
	MemoryPool(unsigned int p_uiMemorySize,unsigned int p_uiUnitSize,unsigned int p_uiUnitInPage,unsigned int p_uiGrowSize);
	
	/**
	* Verify if unit with specified ID is available
	* @param p_uiID ID
	* @return Unit address
	*/	
	char* VerifyID(unsigned int p_uiID);

	/**
	* Allocate one unit.
	* @param p_uiBufferSize Required buffer size
	* @param p_uiID ID
	* @return Memory address Which has been allocated.
	*/	
	char* AllocateSingleUnit(unsigned int p_uiBufferSize, unsigned int p_uiID);

	/**
	* Allocate multiple units.
	* @param p_uiBufferSize Required buffer size
	* @param p_uiID ID
	* @return Memory address Which has been allocated.
	*/	
	char* AllocateMultiUnit(unsigned int p_uiBufferSize, unsigned int p_uiID);
	
	/**
	* Free one unit. To set an unit available
	* @param Indicates if the operation is successful
	*/	
	bool FreeUnit(unsigned int p_uiIndex);

	/**
	* Allocate buffer as growing size
	* @return Head page info of new allocated buffer
	*/	
	PageInfo AllocateGrowBuffer();

	/**
	* [NOT USED]Allocates memory
	*/
	char* Allocate(unsigned int p_uiBufferSize);
private:
	/**
	* Mutex for sync worker thread
	*/
	CRITICAL_SECTION   m_csLock;

	/**
	* Static instance of memory pool
	*/
	static MemoryPool* m_pcMemoryPool;

	/**
	* Memory pool size
	*/
	unsigned int m_uiMemorySize;

	/**
	* One unit size
	*/
	unsigned int m_uiUnitSize;

	/**
	* How many units in one page
	*/
	unsigned int m_uiUnitsInPage;

	/**
	* Growing size for re-allocating
	*/
	unsigned int m_uiGrowSize;

	/**
	* Current index
	*/
	unsigned int m_uiMaxIndex;

	/**
	* Index table container
	*/
	list<IndexTable> m_lstIndexTable;

	/**
	* Page container
	*/
	vector<PageInfo>   m_vecPageInfo;

	/**
	* Total units in the memory pool
	*/
	unsigned int m_uiUnitsCount;

	/**
	* Used units in the memory pool
	*/
	unsigned int m_uiUsedUnits;
};